﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;

namespace ChEngine
{
  /// <summary>
  /// Chessboard based on Bruce Moreland 0x88 schema
  /// the piece array is 128 squares and addressing is
  /// index = rank*16+file
  /// </summary>
  public sealed class ChessBoard
  {
    /// <summary>
    /// Create a new bitboard with the start position...
    /// </summary>
    public ChessBoard()
      : this("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1")
    {

    }
    static ChessBoard()
    {
      InitAttacksArray();
    }


    private int statusStackPointer;
    private static int[] whitePawnsMoves = new int[] { 16, 32, 15, 17 };
    private static int[] blackPawnMoves = new int[] { -16, -32, -15, -17 };
    private static int[] knightMoves = new int[] { -33, -31, -18, -14, 14, 18, 31, 33 };
    private static int[] kingMoves = new int[] { -17, -16, -15, -1, 1, 17, 16, 15 };
    private static int[] diagMoves = new int[] { -15, -17, 17, 15 };
    private static int[] straightMoves = new int[] { -16, -1, 16, 1 };

    private static bool[] straightAttacks = new bool[240];
    private static bool[] diagAttacks = new bool[240];


    private static void InitAttacksArray()
    {

      for (int i = 0; i < 240; ++i)
      {
        int ox88Diff = i - 0x77;
        foreach (int a in diagMoves)
        {
          if (ox88Diff % a == 0)
          {
            diagAttacks[i] = true;
            break;
          }
        }
        foreach (int a in straightMoves)
        {
          if (ox88Diff % a == 0)
          {
            straightAttacks[i] = true;
            break;
          }
        }
      }
    }

#if BITMASK
        private ulong blackMask;
        private ulong whiteMask;

        /// <summary>
        /// Get the little endian bitmask of white pieces 
        /// </summary>
        public ulong WhitePieces
        {
            get
            {
                return whiteMask;
            }
        }
        /// <summary>
        /// Get the little endian bitmask of black pieces 
        /// </summary>
        public ulong BlackPieces
        {
            get
            {
                return blackMask;
            }
        }
#endif
    /// <summary>
    /// Get the pseudo legal moves for the current position. Pseudo legal
    /// means that there is no check for leaving the King in attack, and  
    /// generated castling does not check for having the king walking squares
    /// not under attack. This is for time optimization. In normal engine most of the moves
    /// are discarded, so the check for complete correctness is delayed when the move really need to
    /// executed.
    /// </summary>

    /// <param name="startIndex">The index to begin store the moves in the array</param>
    /// <param name="moves">A big enougth array to collect the moves</param>
    /// <returns>The count of generated moves</returns>
    public int GeneratePseudoLegalMoves(int startIndex, int[] moves)
    {
      int pListPointer, dest, move = 0, ptr, d, i, firstEnemy, lastEnemy, firstSlider, lastSlider, moving, begin = startIndex;
      int[] pawnMoves;
      #region INITIALIZATION
      if (toMove == Side.White)
      {
        firstEnemy = 16;
        lastEnemy = 32;
        pawnMoves = whitePawnsMoves;
        pListPointer = 0;
        firstSlider = firstSliderWhite;
        lastSlider = lastSliderBlack;
      }
      else
      {
        firstEnemy = 0;
        lastEnemy = 16;
        pawnMoves = blackPawnMoves;
        pListPointer = 16;
        firstSlider = firstSliderBlack;
        lastSlider = lastSliderBlack;
      }

      #endregion
      #region PAWNS
      // Pawns
      for (ptr = pListPointer + 1; ptr < pListPointer + 8; ++ptr)
      {
        i = pieceList[ptr] & 0xFF;
        if (0 != (i & 0x88))
          continue;
        dest = i + pawnMoves[0];
        d = board[dest];
        if ((dest & 0x88) == 0 && d == NoPiece)
        {
          if (dest < 112 && dest > 7) // no promotion
          {
            move = i;
            move |= (dest << 8);
            moves[startIndex++] = move;
            dest = i + pawnMoves[1];
            if (board[dest] == NoPiece)
            {
              if (toMove == Side.White && ((i >> 4) == 1))
              {
                move = i;
                move |= (dest << 8);
                moves[startIndex++] = move;
              }
              if (toMove == Side.Black && ((i >> 4) == 6))
              {
                move = i;
                move |= (dest << 8);
                moves[startIndex++] = move;
              }
            }
          }
          else
          {
            move = i;
            move |= (dest << 8);
            moves[startIndex++] = move | Queen << 16;
            moves[startIndex++] = move | Knight << 16;
            moves[startIndex++] = move | Bishop << 16;
            moves[startIndex++] = move | Rook << 16;
          }
        }
        for (int j = 2; j <= 3; ++j)
        {
          dest = i + pawnMoves[j];
          d = board[dest];
          if ((dest & 0x88) == 0
              && ((d >= firstEnemy && d <= lastEnemy) || dest == enPassantSquare))
          {
            if (dest < 112 && dest > 7) // no promotion
            {
              move = i;
              move |= (dest << 8);
              moves[startIndex++] = move;
            }
            else
            {
              move = i;
              move |= (dest << 8);
              moves[startIndex++] = move | Queen << 16;
              moves[startIndex++] = move | Knight << 16;
              moves[startIndex++] = move | Bishop << 16;
              moves[startIndex++] = move | Rook << 16; ;
            }
          }
        }
      }
      #endregion
      #region KNIGHTS
      for (ptr = pListPointer + 8; ptr < pListPointer + 16; ++ptr)
      {
        if (ptr >= firstSlider)
          break;
        i = pieceList[ptr] & 0xFF;
        if (0 != (i & 0x88))
          continue;
        for (int j = 0; j < 8; ++j)
        {
          dest = knightMoves[j] + i;
          if (0 == (dest & 0x88))
          {
            d = board[dest];
            if (d == NoPiece || (d >= firstEnemy && d <= lastEnemy))
            {
              move = i;
              move |= (dest << 8);
              moves[startIndex++] = move;
            }
          }
        }
      }
      #endregion
      #region SLIDERS
      for (ptr = firstSlider; ptr <= lastSlider; ++ptr)
      {
        i = pieceList[ptr] & 0xFF;
        if (0 != (i & 0x88))
          continue;
        moving = (pieceList[ptr] >> 8);
        #region DIAG
        if ((moving & DiagSliding) != 0)
        {
          for (int j = 0; j < 4; ++j)
          {
            dest = i + diagMoves[j];
            while ((dest & 0x88) == 0
                )
            {
              d = board[dest];
              if (!(d == NoPiece || (d >= firstEnemy && d <= lastEnemy)))
                break;
              move = i;
              move |= (dest << 8);
              if ((d >= firstEnemy && d <= lastEnemy))
              {
                moves[startIndex++] = move;
                break;
              }
              moves[startIndex++] = move;
              dest += diagMoves[j];

            }
          }
        }
        #endregion
        #region STRAIGHT
        if ((moving & StraightSliding) != 0)
        {
          for (int j = 0; j < 4; ++j)
          {
            dest = i + straightMoves[j];
            while ((dest & 0x88) == 0

                )
            {
              d = board[dest];
              if (!(d == NoPiece || (d >= firstEnemy && d <= lastEnemy)))
                break;
              move = i;
              move |= (dest << 8);
              if (d >= firstEnemy && d <= lastEnemy)
              {
                moves[startIndex++] = move;
                break;
              }
              moves[startIndex++] = move;
              dest += straightMoves[j];
            }
          }
        }
        #endregion
      }
      #endregion
      #region KING
      i = pieceList[pListPointer] & 0xFF;
      if (0 == (i & 0x88))
      {
        for (int j = 0; j < 8; ++j)
        {
          dest = kingMoves[j] + i;
          if (0 == (dest & 0x88))
          {
            d = board[dest];
            if (!(d == NoPiece || (d >= firstEnemy && d <= lastEnemy)))
              break;
            move = i;
            move |= (dest << 8);
            moves[startIndex++] = move;
          }
        }
        // castlings...

        if (i == (int)Cell.e1 && toMove == Side.White)
        {
          if (0 != (castlingStatus & CastlingAvail.KingSideWhite)
              && board[(int)Cell.f1] == NoPiece
              && board[(int)Cell.g1] == NoPiece
              )
          {
            move = i;
            move |= ((int)Cell.g1 << 8);
            moves[startIndex++] = move;
          }
          if (0 != (castlingStatus & CastlingAvail.QueenSideWhite)
              && board[(int)Cell.d1] == NoPiece
              && board[(int)Cell.c1] == NoPiece
              && board[(int)Cell.b1] == NoPiece
              )
          {
            move = i;
            move |= ((int)Cell.c1 << 8);
            moves[startIndex++] = move;
          }
        }
        else if (i == (int)Cell.e8 && toMove == Side.Black)
        {
          if (0 != (castlingStatus & CastlingAvail.KingSideBlack)
              && board[(int)Cell.f8] == NoPiece
              && board[(int)Cell.g8] == NoPiece
              )
          {
            move = i;
            move |= ((int)Cell.g8 << 8);
            moves[startIndex++] = move;
          }
          if (0 != (castlingStatus & CastlingAvail.QueenSideBlack)
              && board[(int)Cell.d8] == NoPiece
              && board[(int)Cell.c8] == NoPiece
              && board[(int)Cell.b8] == NoPiece
              )
          {
            move = i;
            move |= ((int)Cell.c8 << 8);
            moves[startIndex++] = move;
          }
        }
      }
      #endregion
      return startIndex - begin;
    }

    public ulong CurrentDivideNodeCount { get; set; }
    public string CurrentDivideMove { get; set; }
    /// <summary>
    /// This is the divide command. Used for checking move generator correctness
    /// Results of each divide are available subscribing the DividePartialResult event.
    /// </summary>
    /// <param name="depth">Depth to divide</param>
    /// <returns>The summary divide results</returns>
    public DivideResults Divide(int depth)
    {
      DivideResults div = new DivideResults();
      int[] moves = new int[200];
      div.MovesCount = GeneratePseudoLegalMoves(0, moves);
      for (int i = 0; i < div.MovesCount; ++i)
      {
        if (IsMoveLegal(moves[i]))
        {

          Move(moves[i]);
          if (InAttack(GetKingSquare(toMove == Side.White ? Side.Black : Side.White), toMove))
          {
            UndoMove(moves[i]);
            continue;
          }

          PerfResults pres = Perft(depth - 1);
          div.NodesCount += pres.MovesCount;
          CurrentDivideNodeCount = pres.MovesCount;
          CurrentDivideMove = ToLiteralMove(moves[i]);
          if (null != DividePartialResult)
            DividePartialResult(this, EventArgs.Empty);
          UndoMove(moves[i]);
        }
      }

      return div;
    }
    /// <summary>
    /// Signal a partial results during the "divide" execution.
    /// Used for debug / unit testing the move generator,or to use this generator
    /// as a ource for testing other generators.
    /// Detailed results about whats the "divide" partial results are can be found
    /// in the properties CurrentDivideNodeCount and CurrentDivideMove
    /// </summary>
    public event EventHandler DividePartialResult;

    /// <summary>
    /// Convert a packed move in its algebraic notation ( ex e2e4 ).
    /// No dexoration as + x # are appended to the string.
    /// </summary>
    /// <param name="move">The integer representing the move</param>
    /// <returns>The string representation of the move</returns>
    public string ToLiteralMove(int move)
    {
      string m = FromCellToLiteral((Cell)(move & 0xFF)) + FromCellToLiteral((Cell)((move >> 8) & 0xFF));
      int prom = (move >> 24) & 0xF;
      switch (prom)
      {
        case Queen:
          m += "q";
          break;
        case Rook:
          m += "r";
          break;
        case Bishop:
          m += "b";
          break;
        case Knight:
          m += "n";
          break;
      }
      return m;
    }
    /// <summary>
    /// Restore the board at the status before it moves. This is a low level routine and should not be used
    /// In user interaction.
    /// </summary>
    /// <param name="move">the move to undo</param>
    public void UndoMove(int move)
    {
      int dest = move & 0xFF;
      int start = (move >> 8) & 0xFF;
      if (toMove == Side.Black)
        toMove = Side.White;
      else
        toMove = Side.Black;
      int sindex = board[start];
      int eindex = board[dest];
      int moving = pieceList[sindex] >> 8 & 0xFF;
      board[start] = NoPiece;

      PopStatus();
      int promotion = (capturePromotionStatus >> 16) & 0xFF;
      int captured = capturePromotionStatus >> 4 & 0xF;
      if (promotion == 0)
      {
        board[dest] = eindex;
        pieceList[eindex] = pieceList[sindex];
      }
      else
      {
        #region KNIGHTPROMOTION
        if (promotion == Knight)
        {
          if (toMove == Side.White)
          {
            //shift the piece list one step back
            // update board accordingly...
            for (int i = firstSliderWhite; i < lastSliderWhite; ++i)
            {
              board[i] -= 1;
            }
            // shift piece list
            Array.Copy(pieceList, firstSliderWhite, pieceList, firstSliderWhite - 1, lastSliderWhite - firstSliderWhite);
            //Update pointers
            firstSliderWhite--;
            lastSliderWhite--;
          }
          else
          {
            //shift the piece list one step back
            // update board accordingly...
            for (int i = firstSliderBlack; i < lastSliderBlack; ++i)
            {
              board[i] -= 1;
            }
            // shift piece list
            Array.Copy(pieceList, firstSliderBlack, pieceList, firstSliderBlack - 1, lastSliderBlack - firstSliderBlack);
            //Update pointers
            firstSliderBlack--;
            lastSliderBlack--;
          }
        }
        else
        #endregion
        #region PROMOTETOSLIDER
        {
          if (toMove == Side.White)
          {
            lastSliderWhite--;
          }
          else
          {
            lastSliderBlack--;
          }
        }
        #endregion
        if (toMove == Side.White)
        {
          whiteMaterialValue += PawnValue;
          whiteMaterialValue -= PieceValue(promotion);
          // looks for the first pawn cell 
          for (int i = 1; i < 9; ++i)
          {
            if (pieceList[i] == 0x88)
            {
              pieceList[i] = (short)(dest | (Pawn << 8));
              break;
            }
          }
        }
        else
        {
          blackMaterialValue += PawnValue;
          blackMaterialValue -= PieceValue(promotion);
          // looks for the first pawn cell 
          for (int i = 17; i < 25; ++i)
          {
            if (pieceList[i] == 0x88)
            {
              pieceList[i] = (short)(dest | (Pawn << 8));
              break;
            }
          }
        }
      }
      pieceList[sindex] = 0x88;
      if (captured != 0)
      {
        int destIndex = capturePromotionStatus & 0xFF;
        pieceList[destIndex] = (short)((capturePromotionStatus >> 8) & 0xFF);
        board[dest] = destIndex;
      }
      // castling...
      if (moving == King)
      {
        if (start == (int)Cell.g1 && dest == (int)Cell.e1)
        {
          pieceList[board[(int)Cell.f1]] = (Rook << 8 | (int)Cell.h1);
          board[(int)Cell.h1] = board[(int)Cell.f1];
          board[(int)Cell.f1] = NoPiece;
        }
        if (start == (int)Cell.c1 && dest == (int)Cell.e1)
        {
          pieceList[board[(int)Cell.d1]] = (Rook << 8 | (int)Cell.a1);
          board[(int)Cell.a1] = board[(int)Cell.d1];
          board[(int)Cell.d1] = NoPiece;
        }
        if (start == (int)Cell.g8 && dest == (int)Cell.e8)
        {
          pieceList[board[(int)Cell.f8]] = (Rook << 8 | (int)Cell.h8);
          board[(int)Cell.h8] = board[(int)Cell.f8];
          board[(int)Cell.f8] = NoPiece;
        }
        if (start == (int)Cell.c8 && dest == (int)Cell.e8)
        {
          pieceList[board[(int)Cell.d8]] = (Rook << 8 | (int)Cell.a8);
          board[(int)Cell.a8] = board[(int)Cell.d8];
          board[(int)Cell.d8] = NoPiece;
        }
      }
    }

    const ulong f1h1 = 0xa0UL;
    const ulong a1d1 = 0x9UL;
    const ulong f8h8 = 0xA000000000000000UL;
    const ulong a8d8 = 0x0900000000000000UL;
    /// <summary>
    /// This is the lowest level routine to generate a move. No check about legality
    /// is done ( for performance reason ) so this function is not suitable for user interaction.
    /// </summary>
    /// <param name="move">The move in the packed int format</param>
    public void Move(int move)
    {
      int start = move & 0xFF;
      int dest = (move >> 8) & 0xFF;
      int promotion = (move >> 16) & 0xF;

      bool captureEnPassant = false;
      int sindex = board[start];
      int eindex = board[dest];

      int captured = 0;
      int moving = pieceList[sindex] >> 8;
      if (eindex != NoPiece)
      {
        captured = pieceList[eindex] >> 8;
        capturePromotionStatus = dest;
        capturePromotionStatus |= pieceList[eindex] << 8;

      }
      else
        capturePromotionStatus = 0;

      board[start] = NoPiece;
      if (promotion == 0)
      {
        board[dest] = sindex;
        pieceList[sindex] = (short)dest;
      }
      else
      {
        pieceList[sindex] = 0x88;
        #region KNIGHTPROMOTION
        if (promotion == Knight)
        {
          if (toMove == Side.White)
          {
            //shift the piece list one step
            // update board accordingly...
            for (int i = firstSliderWhite; i < lastSliderWhite; ++i)
            {
              board[i] += 1;
            }
            board[dest] = firstSliderWhite;
            // shift piece list
            Array.Copy(pieceList, firstSliderWhite, pieceList, firstSliderWhite + 1, lastSliderWhite - firstSliderWhite);
            // update PieceList Knight Position
            pieceList[firstSliderWhite] = (short)(dest | (Knight << 8));
            //Update pointers
            firstSliderWhite++;
            lastSliderWhite++;
          }
          else
          {
            //shift the piece list one step
            // update board accordingly...
            for (int i = firstSliderBlack; i < lastSliderBlack; ++i)
            {
              board[i] += 1;
            }
            board[dest] = firstSliderBlack;
            // shift piece list
            Array.Copy(pieceList, firstSliderBlack, pieceList, firstSliderBlack + 1, lastSliderBlack - firstSliderBlack);
            // update PieceList Knight Position
            pieceList[firstSliderBlack] = (short)(dest | (Knight << 8));
            //Update pointers
            firstSliderBlack++;
            lastSliderBlack++;
          }
        }
        else
        #endregion
        #region PROMOTETOSLIDER
        {
          if (toMove == Side.White)
          {
            lastSliderWhite++;
            pieceList[lastSliderWhite] = (short)(dest | promotion << 8);
            board[dest] = lastSliderWhite;
          }
          else
          {
            lastSliderBlack++;
            pieceList[lastSliderBlack] = (short)(dest | promotion << 8);
            board[dest] = lastSliderBlack;
          }
        }
        #endregion
        capturePromotionStatus |= promotion << 16;
        if (toMove == Side.White)
        {
          whiteMaterialValue -= PawnValue;
          whiteMaterialValue += PieceValue(promotion);
        }
        else
        {
          blackMaterialValue -= PawnValue;
          blackMaterialValue += PieceValue(promotion);
        }
      }
      PushStatus();
      if (captured != 0)
      {
        halfMoves = 0;
        if (toMove == Side.White)
        {
          blackMaterialValue -= PieceValue(captured);
        }
        else
        {
          whiteMaterialValue -= PieceValue(captured);
        }
        if (captured == Rook)
        {
          if (dest == (int)Cell.h1)
            castlingStatus &= ~CastlingAvail.KingSideWhite;
          else if (dest == (int)Cell.a1)
            castlingStatus &= ~CastlingAvail.QueenSideWhite;
          else if (dest == (int)Cell.a8)
            castlingStatus &= ~CastlingAvail.QueenSideBlack;
          else if (dest == (int)Cell.h8)
            castlingStatus &= ~CastlingAvail.KingSideBlack;
        }
      }
      // new en passant square..
      int ep = enPassantSquare;
      enPassantSquare = 0x88;
      if (moving == Pawn)
      {
        halfMoves = 0;
        if (dest == ep)
        {
          captureEnPassant = true;

          if (toMove == Side.White)
          {
            blackMaterialValue -= PawnValue;
            capturePromotionStatus = dest - 16;
            capturePromotionStatus |= pieceList[board[dest - 16]] << 8;
            pieceList[board[dest - 16]] = 0x88;
            board[dest - 16] = NoPiece;
          }
          else
          {
            whiteMaterialValue -= PawnValue;
            capturePromotionStatus = dest + 16;
            capturePromotionStatus |= pieceList[board[dest + 16]] << 8;
            pieceList[board[dest + 16]] = 0x88;
            board[dest + 16] = NoPiece;
          }
        }
        if (toMove == Side.White)
        {
          if ((start >> 4) == 1 && (dest >> 4) == 3)
            enPassantSquare = dest - 16;
        }
        else
        {
          if ((start >> 4) == 6 && (dest >> 4) == 4)
            enPassantSquare = dest + 16;
        }
      }
      else
        // castling...
        if (moving == King)
        {

          if (start == (int)Cell.e1 && dest == (int)Cell.g1)
          {
            pieceList[board[(int)Cell.h1]] = (Rook << 8 | (int)Cell.f1);
            board[(int)Cell.f1] = board[(int)Cell.h1];
            board[(int)Cell.h1] = NoPiece;
          }
          if (start == (int)Cell.e1 && dest == (int)Cell.c1)
          {
            pieceList[board[(int)Cell.a1]] = (Rook << 8 | (int)Cell.d1);
            board[(int)Cell.d1] = board[(int)Cell.a1];
            board[(int)Cell.a1] = NoPiece;
          }
          if (start == (int)Cell.e8 && dest == (int)Cell.g8)
          {
            pieceList[board[(int)Cell.h8]] = (Rook << 8 | (int)Cell.f8);
            board[(int)Cell.f8] = board[(int)Cell.h8];
            board[(int)Cell.h8] = NoPiece;
          }
          if (start == (int)Cell.e8 && dest == (int)Cell.c8)
          {
            pieceList[board[(int)Cell.a8]] = (Rook << 8 | (int)Cell.d8);
            board[(int)Cell.d8] = board[(int)Cell.a8];
            board[(int)Cell.a8] = NoPiece;
          }
          if (toMove == Side.White)
            castlingStatus &= ~(CastlingAvail.KingSideWhite | CastlingAvail.QueenSideWhite);
          else
            castlingStatus &= ~(CastlingAvail.KingSideBlack | CastlingAvail.QueenSideBlack);
        }
        else
          // castling status update for rook movements...
          if (moving == Rook)
          {
            if (start == (int)Cell.h1)
              castlingStatus &= ~CastlingAvail.KingSideWhite;
            if (start == (int)Cell.a1)
              castlingStatus &= ~CastlingAvail.QueenSideWhite;
            if (start == (int)Cell.h8)
              castlingStatus &= ~CastlingAvail.KingSideBlack;
            if (start == (int)Cell.a8)
              castlingStatus &= ~CastlingAvail.QueenSideBlack;
          }
      //if (captured != NoPiece && captureEnPassant == false)
      //    pieceList[eindex] = 0x88;
      if (toMove == Side.Black)
      {
        movesCount++;
        toMove = Side.White;
      }
      else
      {
        toMove = Side.Black;
      }
    }

    /// <summary>
    /// This function check if a pseudo legal move is legal
    /// WHITOUT CHECKING FOR KING IN ATTACK!
    /// </summary>
    /// <param name="move">the move to check</param>
    /// <returns></returns>
    public bool IsMoveLegal(int move)
    {
      int start = move & 0xFF;
      int dest = (move >> 8) & 0xFF;
      int moving = (move >> 16) & 0xF;
      Side enemy = toMove == Side.White ? Side.Black : Side.White;
      if (moving == King)
      {
        if (start == (int)Cell.e1 && dest == (int)Cell.g1)
        {
          if (InAttack((int)Cell.f1, enemy)
              ||
              InAttack((int)Cell.g1, enemy)
              ||
              InAttack((int)Cell.e1, enemy)
              )
            return false;
        }
        else if (start == (int)Cell.e1 && dest == (int)Cell.c1)
        {
          if (InAttack((int)Cell.d1, enemy)
              ||
              InAttack((int)Cell.c1, enemy)
              ||
              InAttack((int)Cell.e1, enemy)
              )
            return false;
        }
        else
          if (start == (int)Cell.e8 && dest == (int)Cell.g8)
          {
            if (InAttack((int)Cell.f8, enemy)
                ||
                InAttack((int)Cell.g8, enemy)
                ||
                InAttack((int)Cell.e8, enemy)
                )
              return false;
          }
          else if (start == (int)Cell.e8 && dest == (int)Cell.c8)
          {
            if (InAttack((int)Cell.d8, enemy)
                ||
                InAttack((int)Cell.c8, enemy)
                ||
                InAttack((int)Cell.e8, enemy)
                )
              return false;
          }

      }
      return true;

    }
    private int[] movesForPerft;
    private int perftIndex;
    /// <summary>
    /// This is the classic perft test ( Robert Hyatt and others )
    /// Useful for testing speed and move generator correctness.
    /// 
    /// </summary>
    /// <param name="depth">the depth to reach</param>
    /// <returns>a time elapsed and  moves count</returns>
    public PerfResults Perft(int depth)
    {
      movesForPerft = new int[1000];
      perftIndex = 0;
      Stopwatch sw = new Stopwatch();
      PerfResults perft = new PerfResults();
      sw.Start();
      perft.MovesCount = InternalPerft(depth, perftIndex);
      sw.Stop();
      perft.Elapsed = (ulong)sw.ElapsedMilliseconds;
      return perft;
    }
    private ulong InternalPerft(int depth, int index)
    {
      if (depth == 0)
      {
        return 1;
      }
      ulong count = 0;
      int moveCount = GeneratePseudoLegalMoves(index, movesForPerft);

      for (int i = index; i < index + moveCount; ++i)
      {
        if (IsMoveLegal(movesForPerft[i]))
        {
#if CHECK_UNDO 
                    string check = SavePos();
#endif
          Move(movesForPerft[i]);

#if CHECK_BITMASK && BITMASK
                    ChessBoard bTest = new ChessBoard(SavePos());
                    Debug.Assert(bTest.WhitePieces == WhitePieces);
                    Debug.Assert(bTest.BlackPieces == BlackPieces);
#endif

          if (InAttack(GetKingSquare(toMove == Side.White ? Side.Black : Side.White), toMove))
          {
            UndoMove(movesForPerft[i]);
#if CHECK_BITMASK && BITMASK
                        bTest = new ChessBoard(SavePos());
                        Debug.Assert(bTest.WhitePieces == WhitePieces);
                        Debug.Assert(bTest.BlackPieces == BlackPieces);
#endif
#if CHECK_UNDO
                        Debug.Assert(SavePos()==check,string.Format("Wrong fen after undo:{0}\nPrevious fen={1}\nMove={2}",SavePos(),check,this.ToLiteralMove(movesForPerft[i])));
#endif
            continue;
          }
          ulong n = InternalPerft(depth - 1, index + moveCount);
          count += n;
          UndoMove(movesForPerft[i]);
#if CHECK_BITMASK && BITMASK
                    bTest = new ChessBoard(SavePos());
                    Debug.Assert(bTest.WhitePieces == WhitePieces);
                    Debug.Assert(bTest.BlackPieces == BlackPieces);
#endif
#if CHECK_UNDO
                    Debug.Assert(SavePos() == check, string.Format("Wrong fen after undo:{0}\nPrevious fen={1}\nMove={2}", SavePos(), check, this.ToLiteralMove(movesForPerft[i])));
#endif

        }
      }
      return count;
    }

    public List<int> GetLegalMoves()
    {
      var res = new List<int>();
      var buf = new int[50];
      var count = GeneratePseudoLegalMoves(0, buf);


      for (var i = 0; i < count; ++i)
      {
        var move = buf[i];
        if (!IsMoveLegal(move)) continue;
        Move(move);
        if (!InAttack(GetKingSquare(toMove == Side.White ? Side.Black : Side.White), toMove))
          res.Add(move);

        UndoMove(move);
      }
      return res;
    }

    /// <summary>
    /// Returns if a square is attacked by a side.
    /// </summary>
    /// <param name="index">The 0x88 index of the square</param>
    /// <param name="fromSide">the side to check if attacking square</param>
    /// <returns>true if the square is attacked by fromSide</returns>
    public bool InAttack(int index, Side fromSide)
    {
      /*
      int[] pawnMoves; 
      int enemy; 
      int movingSide; 
      int[] pieceList; 
      int pawnAttackRank;
      int dest, i;
      if( fromSide == Side.Black )
      {
          pawnMoves = blackPawnMoves;
          enemy = Black;
          movingSide = White;
          pawnAttackRank = (index + 16)&0xF0;
          pieceList = blackPieces;
      }
      else
      {
          enemy = White;
          pawnMoves = whitePawnsMoves;
          movingSide = Black;
          pawnAttackRank = (index - 16)&0xF0;
          pieceList = whitePieces;
      }
            
      for (i = 0; i < pieceList.Length; ++i)
      {
          int opponentSquare = pieceList[i];
          if ((opponentSquare & 0x88) != 0)
              continue;
          int opponent = board[opponentSquare] & PieceMask;
          if (opponent == Pawn
              &&
              (opponentSquare & 0xF0)==pawnAttackRank 
              )
          {
              for (int j = 2; j <= 3; ++j)
              {
                  dest = pawnMoves[j] + opponentSquare;
                  if (dest == index)
                      return true;
              }
          }
          else if (opponent == King)
          {
              for (int j = 0; j < 8; ++j)
              {
                  dest = kingMoves[j] + opponentSquare;
                  if (dest == index)
                      return true;
              }
          }
          else if (opponent == Knight)
          {
              for (int j = 0;  j < 8; ++j)
              {
                  dest = knightMoves[j] + opponentSquare;
                  if (dest == index)
                      return true;
              }
          }
                
          if ((opponent & IsSliding) == 0)
              continue;
          if ((opponent & DiagSliding) != 0
              && diagAttacks[0x77 + opponentSquare - index]
              )
          {
              for (int j = 0; j < 4; ++j)
              {
                  dest = opponentSquare + diagMoves[j];
                  while ((dest & 0x88) == 0 && (board[dest] & enemy) == 0)
                  {
                      if( dest == index )
                          return true;
                      if ((board[dest] & movingSide) != 0)
                          break;
                      dest += diagMoves[j];
                  }
              }
          }
          if ((opponent & StraightSliding) != 0
              &&  straightAttacks[0x77 + opponentSquare - index] 
              )
          {
              for (int j = 0; j < 4; ++j)
              {
                  dest = opponentSquare + straightMoves[j];
                  while ((dest & 0x88) == 0 && (board[dest] & enemy) == 0)
                  {
                      if (dest == index)
                      {
                          return true;
                      }
                      if ((board[dest] & movingSide) != 0)
                          break;
                      dest += straightMoves[j];
                  }
              }
          }
      }
      */

      return false;
    }
    /// <summary>
    /// This is the perf test, usefull to benchmark move generation
    /// and move / undo move performances
    /// </summary>
    /// <param name="undoRedo">The results for doing undoing moves...</param>
    /// <param name="nofLoops">The loop count to run the generation </param>
    /// <returns></returns>
    public PerfResults Perf(out PerfResults undoRedo, int nofLoops)
    {
      undoRedo = new PerfResults();
      int[] moves = new int[100];
      PerfResults res = new PerfResults();
      Stopwatch sw = new Stopwatch();
      sw.Start();
      for (int i = 0; i < nofLoops; ++i)
      {
        res.MovesCount += (ulong)GeneratePseudoLegalMoves(0, moves);
      }
      sw.Stop();
      res.Elapsed = (ulong)sw.ElapsedMilliseconds;
      sw.Start();
      for (int i = 0; i < nofLoops; ++i)
      {
        int n = GeneratePseudoLegalMoves(0, moves);
        undoRedo.MovesCount += (ulong)n;
        for (int m = 0; m < n; ++m)
        {
          Move(moves[m]);
          UndoMove(moves[m]);
        }
      }
      sw.Stop();
      undoRedo.Elapsed = (ulong)sw.ElapsedMilliseconds;
      return res;
    }
    /// <summary>
    /// returns the square in wich the king is...
    /// </summary>
    /// <param name="s">The color of the king needed</param>
    /// <returns>the ox88 index of the king square</returns>
    public int GetKingSquare(Side s)
    {
      if (s == Side.White)
        return pieceList[0];
      else
        return pieceList[16];
    }

    int capturePromotionStatus;
    private void PushStatus()
    {
      ulong status = 0;
      status |= (ulong)(uint)castlingStatus;
      status |= (ulong)(uint)(enPassantSquare << 4);
      status |= (ulong)(uint)(halfMoves << 12);
      status |= (ulong)(uint)(movesCount << 22);
      status |= (ulong)(uint)(capturePromotionStatus << 32);
      statusStack[statusStackPointer++] = status;
    }
    private void PopStatus()
    {
      ulong status = statusStack[--statusStackPointer];
      castlingStatus = (CastlingAvail)(status & 0xF);
      enPassantSquare = (int)(status >> 4) & 0xFF;
      halfMoves = (int)(status >> 12) & 0x3FF;
      movesCount = (int)(status >> 22) & 0x3FF;
      capturePromotionStatus = (int)(status >> 32);
    }


    private int PieceValue(int piece)
    {
      switch (piece)
      {
        case Pawn:
          return PawnValue;
        case Knight:
          return KnightValue;
        case Bishop:
          return BishopValue;
        case Rook:
          return RookValue;
        case Queen:
          return QueenValue;
        case King:
          return KingValue;
        default:
          return 0;
      }
    }

    /// <summary>
    /// Create a bitboard for a given position
    /// </summary>
    /// <param name="fen">Forsyth-Edwards Notation (FEN) string describing the position</param>
    public ChessBoard(string fen)
    {
      SetBoard(fen);
      statusStackPointer = 0;
    }

    private void ClearBoard()
    {
      for (int i = 0; i < 128; ++i)
        board[i] = NoPiece;
    }

    private void InitPieceList()
    {
      for (int i = 0; i < 32; ++i)
      {
        pieceList[i] = 0x88;
      }
    }
    /// <summary>
    /// return the side that has the move
    /// </summary>
    Side ToMove
    {
      get { return toMove; }
    }

    public int WhiteMaterialValue
    {
      get { return whiteMaterialValue; }
    }
    public int BlackMaterialValue
    {
      get { return blackMaterialValue; }
    }
    /// <summary>
    /// Force a different side to have the move. Just for special commands.
    /// Should not be used...
    /// </summary>
    /// <param name="newSide"></param>
    public void ForceSide(Side newSide)
    {
      toMove = newSide;
    }

    Stack<int> movesStack = new Stack<int>();

    /// <summary>
    /// Do a move in the form "e2e4(qrnb)". This function check for move availability
    /// and avoid leaving king in attack.
    /// So it is suitable for user input.
    /// This function will trow an exception if the move is not acceptable or the
    /// move string cannot be parsed.
    /// </summary>
    /// <param name="p"></param>
    public void Move(string p)
    {
      int[] avail = new int[200];
      int n = GeneratePseudoLegalMoves(0, avail);
      Regex r = new Regex("(?'s'[abcdefgh][12345678])(?'e'[abcdefgh][12345678])(?'p'[qrbn]?)");
      Match m = r.Match(p);
      if (m.Success == false)
        throw new ChessEngineRecoverableException("Unrecognized move:" + p);
      Cell start = FromLiteralCell(m.Groups["s"].Value);
      Cell end = FromLiteralCell(m.Groups["e"].Value);
      string promotion = string.Empty;
      if (m.Groups["p"] != null)
      {
        promotion = m.Groups["p"].Value;
      }
      int found = 0;
      int template = (int)start | (int)end << 8;
      foreach (int move in avail)
      {
        if ((move & 0xFFFF) == template)
        {
          if (promotion == string.Empty)
          {
            found = move;
            break;
          }
          else
          {
            int piece = Queen;
            switch (promotion)
            {
              case "q":
                piece = Queen;
                break;
              case "r":
                piece = Rook;
                break;
              case "b":
                piece = Bishop;
                break;
              case "n":
                piece = Knight;
                break;
            }
            if ((move >> 24 & 0xF) == piece)
            {
              found = move;
              break;
            }
          }
        }
      }
      if (found == 0)
      {
        throw new ChessEngineRecoverableException("Move not available:" + p);
      }
      if (IsMoveLegal(found))
      {
        Move(found);
        if (InAttack(GetKingSquare(toMove == Side.White ? Side.Black : Side.White), toMove))
        {
          UndoMove(found);
          throw new ChessEngineRecoverableException("Invalid move,king is in check!:" + p);
        }
        movesStack.Push(found);
      }
      else
        throw new Exception("Move not available:" + p);



    }


    private string GetPieceString(int p)
    {
      string pp = "";
      bool white = 0 != (p & White);
      p &= PieceMask;
      if (p == Pawn)
      {
        pp = "p";
      }
      if (p == Knight)
      {
        pp = "n";
      }
      if (p == Bishop)
      {
        pp = "b";
      }
      if (p == Rook)
      {
        pp = "r";
      }
      if (p == Queen)
      {
        pp = "q";
      }
      if (p == King)
      {
        pp = "k";
      }
      if (white)
      {
        pp = pp.ToUpper();
      }
      return pp;
    }

    #region piece values
    public const int PawnValue = 100;
    public const int KnightValue = 300;
    public const int BishopValue = 300;
    public const int RookValue = 500;
    public const int QueenValue = 900;
    public const int KingValue = 1000000;
    #endregion
    /// <summary>
    /// Get the Fn string for the board. The reason the fuunction is not named as GetFen()
    /// Is just because "savepos" is the normal command is associated to this function in 
    /// chess programming.
    /// </summary>
    /// <returns>The Forsyth-Edwards Notation (FEN) string describing the position</returns>
    public string SavePos()
    {
      StringBuilder sb = new StringBuilder();
      SaveParts(sb);
      sb.Append(" ");
      SaveToMove(sb);
      sb.Append(" ");
      SaveCastling(sb);
      sb.Append(" ");
      SaveEnPassant(sb);
      sb.Append(" ");
      sb.Append(halfMoves.ToString());
      sb.Append(" ");
      sb.Append(movesCount.ToString());
      return sb.ToString();
    }
    #region Fen Saving...
    private void SaveEnPassant(StringBuilder sb)
    {
      sb.Append(FromCellToLiteral((Cell)enPassantSquare));
    }

    private void SaveCastling(StringBuilder sb)
    {
      if (castlingStatus == CastlingAvail.None)
        sb.Append("-");
      else
      {
        if (0 != (castlingStatus & CastlingAvail.KingSideWhite))
        {
          sb.Append("K");
        }
        if (0 != (castlingStatus & CastlingAvail.QueenSideWhite))
        {
          sb.Append("Q");
        }
        if (0 != (castlingStatus & CastlingAvail.KingSideBlack))
        {
          sb.Append("k");
        }
        if (0 != (castlingStatus & CastlingAvail.QueenSideBlack))
        {
          sb.Append("q");
        }
      }
    }

    private void SaveToMove(StringBuilder sb)
    {
      if (toMove == Side.White)
        sb.Append("w");
      else
        sb.Append("b");
    }

    private void SaveParts(StringBuilder sb)
    {
      int emptyCount = 0;
      for (int i = 112; i >= 0; i -= 16)
      {
        for (int ii = i; ii < i + 9; ++ii)
        {
          if (0 != (ii & 0x88))
          {
            if (emptyCount > 0)
              sb.Append(emptyCount);
            if (i > 0)
              sb.Append("/");
            emptyCount = 0;
            break;
          }
          if (board[ii] == NoPiece)
          {
            emptyCount++;
          }
          else
          {
            if (emptyCount > 0)
            {
              sb.Append(emptyCount);
              emptyCount = 0;
            }
            sb.Append(GetPieceString(board[ii]));
          }
        }
      }
    }
    #endregion

    /// <summary>
    /// Set the board at a specified fen
    /// </summary>
    /// <param name="fen">Forsyth-Edwards Notation (FEN) string describing the position</param>
    public void SetBoard(string fen)
    {
      if (fen == null)
        throw new ArgumentNullException("fen");
      InitPieceList();
      ClearBoard();

      movesStack = new Stack<int>();
      string[] fenParts = fen.Trim().Split(' ');
      for (int i = 0; i < board.Length; ++i)
      {
        board[i] = NoPiece;
      }
      whiteMaterialValue = 0;
      blackMaterialValue = 0;
      halfMoves = 0;
      movesCount = 0;
      castlingStatus = CastlingAvail.None;
      enPassantSquare = (int)Cell.Invalid;
      if (fenParts.Length != 6)
        throw new Exception("Invalid fen:" + fen);
      ParseParts(fenParts[0]);
      ParseToMove(fenParts[1]);
      ParseCastling(fenParts[2]);
      ParseEnPassant(fenParts[3]);
      if (false == int.TryParse(fenParts[4], out halfMoves))
        throw new Exception("Half moves count cannot be parsed:" + fenParts[4]);
      if (false == int.TryParse(fenParts[5], out movesCount))
        throw new Exception("Moves count cannot be parsed:" + fenParts[5]);
      if (movesCount < 1)
        throw new Exception("Moves count less than 1:" + fenParts[5]);
    }
    #region fen parsing privates
    private void ParseEnPassant(string p)
    {
      if (p == "-")
        return;
      FieldInfo fi = typeof(Cell).GetField(p);
      if (fi == null)
        throw new Exception("Invalid Fen cell en passant specification:" + p);
      enPassantSquare = (int)fi.GetRawConstantValue();
    }

    private void ParseCastling(string p)
    {
      foreach (char c in p)
      {
        switch (c)
        {
          case 'k':
            castlingStatus |= CastlingAvail.KingSideBlack;
            break;
          case 'K':
            castlingStatus |= CastlingAvail.KingSideWhite;
            break;
          case 'q':
            castlingStatus |= CastlingAvail.QueenSideBlack;
            break;
          case 'Q':
            castlingStatus |= CastlingAvail.QueenSideWhite;
            break;
        }
      }
    }

    private void ParseToMove(string p)
    {
      if (p == "w")
        toMove = Side.White;
      else
        toMove = Side.Black;
    }
    /// <summary>
    /// Helper stuct to collect piece in lists parsing the fen.
    /// Collecting lists make easier build the subsequential pice list array
    /// </summary>
    struct FenPiece : IComparable
    {
      public int cell;
      public int piece;

      #region IComparable Members

      public int CompareTo(object obj)
      {
        FenPiece other = (FenPiece)obj;
        return GetComparableValue() - other.GetComparableValue();
      }

      #endregion
      private int GetComparableValue()
      {
        switch (piece)
        {
          case Pawn:
            return 1;
          case King:
            return 0;
          case Knight:
            return 2;
          case Bishop:
            return 4;
          case Rook:
            return 5;
          case Queen:
            return 3;
          default:
            throw new Exception("Internal error:Unknown piece code");
        }
      }
    }
    private void ParseParts(string parts)
    {
      List<FenPiece> whites = new List<FenPiece>();
      List<FenPiece> blacks = new List<FenPiece>();
      int rank = 7;
      int file = 1;
      FenPiece piece;
      foreach (char c in parts)
      {
        if (char.IsLetter(c))
        {
          int side = char.IsLower(c) ? Black : White;
          int partValue = 0;
          char p = char.ToLower(c);
          int f = file - 1;
          List<FenPiece> current;
          if (side == Black)
            current = blacks;
          else
            current = whites;
          switch (p)
          {
            case 'p':
              piece = new FenPiece();
              piece.piece = Pawn;
              piece.cell = rank * 16 + f;
              current.Add(piece);
              partValue += PawnValue;
              break;
            case 'n':
              piece = new FenPiece();
              piece.piece = Knight;
              piece.cell = rank * 16 + f;
              current.Add(piece);
              partValue += KnightValue;
              break;
            case 'b':
              piece = new FenPiece();
              piece.piece = Bishop;
              piece.cell = rank * 16 + f;
              current.Add(piece);
              partValue += BishopValue;
              break;
            case 'r':
              piece = new FenPiece();
              piece.piece = Rook;
              piece.cell = rank * 16 + f;
              current.Add(piece);
              partValue += RookValue;
              break;
            case 'q':
              piece = new FenPiece();
              piece.piece = Queen;
              piece.cell = rank * 16 + f;
              current.Add(piece);
              partValue += RookValue;
              partValue += QueenValue;
              break;
            case 'k':
              piece = new FenPiece();
              piece.piece = King;
              piece.cell = rank * 16 + f;
              current.Add(piece);
              partValue += RookValue;
              partValue += KingValue;
              break;
          }
          if (side == Black)
          {
            blackMaterialValue += partValue;
          }
          else
          {
            whiteMaterialValue += partValue;
          }
          file++;
        }
        if (char.IsDigit(c))
          file += int.Parse(new string(c, 1));
        else
          if (c == '/')
          {
            file = 1;
            rank--;
          }
      }
      whites.Sort();
      blacks.Sort();
      firstSliderWhite = CreatePieceList(0, whites, out lastSliderWhite);
      firstSliderBlack = CreatePieceList(16, blacks, out lastSliderBlack);
    }

    private int CreatePieceList(int p, List<FenPiece> pieces, out int lastSlider)
    {
      int i = p;
      int pawnEnd = i + 9;
      int firstSlider = 33;
      lastSlider = 0;
      foreach (FenPiece fp in pieces)
      {
        if (fp.piece != Pawn && fp.piece != King && i < pawnEnd)
          i = pawnEnd;
        pieceList[i] = (short)fp.cell;
        pieceList[i] |= (short)(fp.piece << 8);
        board[fp.cell] = i;
        if (0 != (fp.piece & IsSliding) && firstSlider == 33)
        {
          firstSlider = i;
        }
        i++;
      }
      if (firstSlider != 33)
        lastSlider = i - 1;

      return firstSlider;
    }
    #endregion
    /// <summary>
    /// Get the castling availability status
    /// </summary>
    public CastlingAvail CastlingStatus
    {
      get { return castlingStatus; }
    }
    /// <summary>
    /// Get the en passant cell for the board. Could be invalid value
    /// casting to int returns a cell index based 0n 0x88 little endian board representation
    /// </summary>
    public Cell EnPassantIndex
    {
      get { return (Cell)enPassantSquare; }
    }
    /// <summary>
    /// Convert a cell in 0x88 representation in an index 0..63 where a1 is 0 and h8 is 63
    /// </summary>
    /// <param name="cell">The ox88 cell index</param>
    /// <returns>the 8x8 cell index ( -1 if invalid cell )</returns>
    public static int FromCellTo8x8(Cell cell)
    {
      if (0 != (cell & Cell.Invalid))
        return -1;
      else
      {
        int idx = (int)cell;
        return (idx >> 4) * 8 + (idx & 0xF);
      }
    }
    /// <summary>
    /// return a cell in the format a1,c3,h8 etc in the 0x88 index
    /// </summary>
    /// <param name="cell">the cell string</param>
    /// <returns>the 0x88 cell</returns>
    public static Cell FromLiteralCell(string cell)
    {
      if (!Regex.IsMatch(cell, "[a-h][1-8]"))
        throw new Exception("Invalid cell specification:" + cell);
      string files = "abcdefgh";
      int file = files.IndexOf(cell.Substring(0, 1));
      int rank = int.Parse(cell.Substring(1, 1)) - 1;
      return (Cell)(rank * 16 + file);
    }
    /// <summary>
    /// returns a cell in the format a1,c3,h8 etc in the 0x88 index from Cell enum in 0x88 format
    /// </summary>
    /// <param name="cell"></param>
    /// <returns></returns>
    public static string FromCellToLiteral(Cell cell)
    {
      if (cell == Cell.Invalid)
        return "-";
      string files = "abcdefgh";
      int idx8x8 = FromCellTo8x8(cell);
      if (idx8x8 == -1)
        return "-";
      return files.Substring(idx8x8 % 8, 1) + ((idx8x8 / 8) + 1).ToString();
    }

    /// <summary>
    /// Get the moves count. The count is incremented at each black move and starts from 1.
    /// </summary>
    public int MovesCount
    {
      get { return movesCount; }
    }
    /// <summary>
    /// Gets the number of halfmoves since the last pawn advance or capture.
    /// </summary>
    public int HalfMovesCount
    {
      get { return halfMoves; }
    }
    int whiteMaterialValue;
    int blackMaterialValue;
    int halfMoves; // 50 moves drawing rule...
    int movesCount;
    int[] board = new int[128];
    ulong[] statusStack = new ulong[1000];
    Side toMove;
    int enPassantSquare; // invalid square
    CastlingAvail castlingStatus;


    /// <summary>
    /// This function is an user callable UndoMove, that rollback a move
    /// done by Move( string )
    /// </summary>
    public void UndoMove()
    {
      if (movesStack.Count == 0)
        throw new ChessEngineRecoverableException("Moves stack underflow:nothing to undo");
      else
        UndoMove(movesStack.Pop());
    }
    /// The piece containined in a square
    /// To describe the part only 4 bit is necessary ( this is significant only for move packing )
    /// The part color is maskable with the part.
    public const int PieceMask = 0xF;
    public const int NoPiece = -1;
    public const int Pawn = 0x1;
    public const int Knight = 2;
    public const int King = 3;
    public const int Rook = 4;
    public const int Bishop = 8;
    public const int Queen = 12;
    public const int IsSliding = 12;
    public const int DiagSliding = 8;
    public const int StraightSliding = 4;
    public const int White = 16;
    public const int Black = 32;




    /// <summary>
    /// Dump a board in a form suitable for a console application.
    /// </summary>
    /// <param name="tw">The text writer to dump the board</param>
    public void Dump(TextWriter tw)
    {
      string row = " +---+---+---+---+---+---+---+---+";
      for (int i = 8; i > 0; --i)
      {
        tw.WriteLine(row);
        tw.WriteLine(i.ToString() + LiteralPartInRank(i - 1));
      }
      tw.WriteLine(row);
      tw.WriteLine("   a   b   c   d   e   f   g   h");
    }
    private string LiteralPartInRank(int nRank)
    {
      StringBuilder sb = new StringBuilder();
      for (int file = 0; file < 8; ++file)
      {
        sb.Append('|');
        string part = " · ";
        switch (board[nRank * 16 + file] & PieceMask)
        {
          case Pawn:
            part = " P ";
            break;
          case Bishop:
            part = " B ";
            break;
          case Knight:
            part = " N ";
            break;
          case Rook:
            part = " R ";
            break;
          case Queen:
            part = " Q ";
            break;
          case King:
            part = " K ";
            break;
        }
        if ((board[nRank * 16 + file] & Black) != 0)
          part = part.ToLower();
        sb.Append(part);
      }
      sb.Append("|");
      return sb.ToString();
    }
    // Piece List:
    // 0-16 = White pieces 16-32 = black pieces 
    // offsets: 0 = King 1-8 = Pawns then leapers and sliders ( offset is indexed by firstSliderBlack / firstSliderWhite
    int firstSliderBlack;
    int firstSliderWhite;
    int lastSliderWhite;
    int lastSliderBlack;

    short[] pieceList = new short[32];
  }
}
